package org.goobs.chess;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import org.goobs.io.Console;

public class ChessPosition implements Comparable<ChessPosition> {

	public class PositionInformation {
		private int color;
		private boolean reversed = false;
		private boolean pruned = false;
		private Collection<ChessPosition> children;
		private Collection<ChessPosition> moveAgainChildren;
		private long attackMatrix;
		private long defendMatrix;

		private PositionInformation(int color) {
			this.color = color;
		}
		
		private final void reverse(){
			PositionInformation reMove = calculateInformation(opponent(color));
			this.defendMatrix = reMove.attackMatrix;
			this.moveAgainChildren = reMove.children;
			reversed = true;
		}

		private void pruneForChecks(){
			Iterator <ChessPosition> iter = children.iterator();
			while(iter.hasNext()){
				ChessPosition pos = iter.next();
				char x = pos.getPieceX(KING_INDEX, color);
				int y = pos.getPieceY(KING_INDEX, color);
				if(isAttackedByOpponent(x, y)){
					iter.remove();
				}
			}
			pruned = true;
		}
		
		public final Collection<ChessPosition> getChildren() {
			if(!pruned){
//				pruneForChecks();
			}
			return children;
		}
		
		public final Collection<ChessPosition> getMoveAgainChildren() {
			return moveAgainChildren;
		}

		public final long getAttackMatrix() {
			return attackMatrix;
		}
		
		public final long getDefendMatrix(){
			return defendMatrix;
		}

		public final long getOpponentOnPrixMatrix(){
			return (attackMatrix ^ defendMatrix) & attackMatrix;
		}
		
		public final long getSelfOnPrixMatrix(){
			return (attackMatrix ^ defendMatrix) & defendMatrix;
		}
		
		public final ChessPosition getPosition(){
			return ChessPosition.this;
		}
		
		public final boolean isAttackedByUs(char x, int y) {
			return isTrue(attackMatrix, x, y);
		}
		
		public final boolean isAttackedByOpponent(char x, int y){
			//(calculate reverse information)
			if(!reversed){
				reverse();
			}
			//(find result)
			return isTrue(defendMatrix, x, y);
		}

		public final int getPoints(int color) {
			int sum = 0;
			for (int i = 0; i < 16; i++) {
				char row = getPieceX(i, color);
				if (row != NO_X_COR) {
					sum += getPointVal(i);
				}
			}
			return sum;
		}
		
		public final int getTakablePoints(){
			int points = 0;
			long onPrix = getOpponentOnPrixMatrix();
			for(int i=0; i<16; i++){
				char x = getPieceX(i, opponent(color));
				int y = getPieceY(i, opponent(color));
				if(isTrue(onPrix, x, y)){
					points += getPointVal(i);
				}
			}
			return points;
		}
		
		public final void printAttackMatrix() {
			for (int y = 8; y >= 1; y--) {
				for (char x = 'a'; x <= 'h'; x++) {
					if (isAttackedByUs(x, y)) {
						System.out.print("| x ");
					} else {
						System.out.print("|   ");
					}
				}
				System.out.println("|");
				System.out.println();
			}
		}
		
		public final void printDefendMatrix() {
			for (int y = 8; y >= 1; y--) {
				for (char x = 'a'; x <= 'h'; x++) {
					if (isAttackedByOpponent(x, y)) {
						System.out.print("| o ");
					} else {
						System.out.print("|   ");
					}
				}
				System.out.println("|");
				System.out.println();
			}
		}

		public final void printAttackDefendMatrix() {
			for (int y = 8; y >= 1; y--) {
				for (char x = 'a'; x <= 'h'; x++) {
					if(isAttackedByUs(x, y) && isAttackedByOpponent(x,y)){
						System.out.print("| * ");
					}else if (isAttackedByOpponent(x, y)) {
						System.out.print("| o ");
					}else if(isAttackedByUs(x,y)){
						System.out.print("| x ");
					} else {
						System.out.print("|   ");
					}
				}
				System.out.println("|");
				System.out.println();
			}
		}
		
	}
	
	// (color)
	public static final int BLACK = ChessAgent.BLACK;
	public static final int WHITE = ChessAgent.WHITE;

	// (data field masks)
	private static final short PIECE_COLOR = 0x80;
	private static final short PIECE_COLOR_CLR = 0x7F;
	private static final short PIECE_TYPE = 0x70;
	private static final short PIECE_TYPE_CLR = 0x8F;
	private static final short PIECE_COR = 0x0F;
	private static final short PIECE_COR_CLR = 0xF0;

	// (piece types)
	private static final int NO_TYPE = 0;
	private static final int PAWN = 1;
	private static final int ROOK = 2;
	private static final int KNIGHT = 3;
	private static final int BISHOP = 4;
	private static final int QUEEN = 5;
	private static final int KING = 6;

	// (coordinate helpers)
	private static final char NO_X_COR = 'i';
	private static final int NO_Y_COR = 9;
	private static final int KING_INDEX = 4;

	// (fields)
	private int score;
	private byte[] data;

	public ChessPosition() {
		this(true);
	}

	private ChessPosition(boolean isStart) {
		if (isStart) {
			data = new byte[64];
			// (white pieces)
			registerPiece(0, 'a', 1, ROOK, WHITE);
			registerPiece(1, 'b', 1, KNIGHT, WHITE);
			registerPiece(2, 'c', 1, BISHOP, WHITE);
			registerPiece(3, 'd', 1, QUEEN, WHITE);
			registerPiece(KING_INDEX, 'e', 1, KING, WHITE);
			registerPiece(5, 'f', 1, BISHOP, WHITE);
			registerPiece(6, 'g', 1, KNIGHT, WHITE);
			registerPiece(7, 'h', 1, ROOK, WHITE);
			for (char col = 'a'; col <= 'h'; col++) {
				registerPiece(8 + (col - 'a'), col, 2, PAWN, WHITE);
			}
			// (black pieces)
			registerPiece(0, 'a', 8, ROOK, BLACK);
			registerPiece(1, 'b', 8, KNIGHT, BLACK);
			registerPiece(2, 'c', 8, BISHOP, BLACK);
			registerPiece(3, 'd', 8, QUEEN, BLACK);
			registerPiece(KING_INDEX, 'e', 8, KING, BLACK);
			registerPiece(5, 'f', 8, BISHOP, BLACK);
			registerPiece(6, 'g', 8, KNIGHT, BLACK);
			registerPiece(7, 'h', 8, ROOK, BLACK);
			for (char col = 'a'; col <= 'h'; col++) {
				registerPiece(8 + (col - 'a'), col, 7, PAWN, BLACK);
			}
		}
	}
	
	private ChessPosition(int junk){
		data = new byte[64];
		vanishAll();
		registerPiece(5, 'e', 5, BISHOP, WHITE);
		registerPiece(8, 'f', 6, PAWN, BLACK);
		registerPiece(9, 'g', 7, PAWN, BLACK);
	}

	private final int getPieceColor(char x, int y) {
		int index = 8 * (x - 'a') + (y - 1);
		return (data[index] & PIECE_COLOR) >> 7;
	}

	private final void registerPiece(int index, char x, int y, int type,
			int color) {
		setPieceColor(x, y, color);
		setPieceType(x, y, type);
		setPieceX(index, color, x);
		setPieceY(index, color, y);
	}
	
	private final void vanishPiece(int pieceIndex, int color){
		char x = getPieceX(pieceIndex, color);
		int y = getPieceY(pieceIndex, color);
		int type = getPieceType(x, y);
		if(type != NO_TYPE){
			setPieceType(x,y,NO_TYPE);
		}
		setPieceX(pieceIndex, color, NO_X_COR);
		setPieceY(pieceIndex, color, NO_Y_COR);
	}
	
	private final void vanishAll(){
		for(int i=0; i<16; i++){
			vanishPiece(i, WHITE);
			vanishPiece(i, BLACK);
		}
	}

	private final void setPieceColor(char x, int y, int color) {
		int index = 8 * (x - 'a') + (y - 1);
		data[index] = (byte) (data[index] & PIECE_COLOR_CLR);
		data[index] = (byte) (data[index] | (color << 7));
	}

	private final int getPieceType(char x, int y) {
		int index = 8 * (x - 'a') + (y - 1);
		return (data[index] & PIECE_TYPE) >> 4;
	}

	private final void setPieceType(char x, int y, int type) {
		int index = 8 * (x - 'a') + (y - 1);
		data[index] = (byte) (data[index] & PIECE_TYPE_CLR);
		data[index] = (byte) (data[index] | (type << 4));
	}

	private final char getPieceX(int pieceIndex, int color) {
		int index = 32 * color + 2 * pieceIndex;
		return (char) ('a' + (data[index] & PIECE_COR));
	}

	private final void setPieceX(int pieceIndex, int color, char x) {
		int index = 32 * color + 2 * pieceIndex;
		data[index] = (byte) (data[index] & PIECE_COR_CLR);
		data[index] = (byte) (data[index] | (x - 'a'));
		// System.out.println(index + " " + ((data[index] & PIECE_COR) >> 1));
	}

	private final int getPieceY(int pieceIndex, int color) {
		int index = 32 * color + 2 * pieceIndex + 1;
		return (data[index] & PIECE_COR) + 1;
	}

	private final void setPieceY(int pieceIndex, int color, int y) {
		int index = 32 * color + 2 * pieceIndex + 1;
		data[index] = (byte) (data[index] & PIECE_COR_CLR);
		data[index] = (byte) (data[index] | (y - 1));
		// System.out.println(index + " " + ((data[index] & PIECE_COR) >> 1));
	}

	private final long setAttacked(long attackMatrix, char x, int y) {
		int shift = 8 * (x - 'a') + (y - 1);
		long mask = 0x0000000000000001L << shift;
		return attackMatrix | mask;
	}

	private final void internalMove(char x0, int y0, char x1, int y1) {
		// --Get relevant information
		int type = getPieceType(x0, y0);
		int color = getPieceColor(x0, y0);
		int takenType = getPieceType(x1, y1);

		// --Update the moving piece's location
		if (type == PAWN) {
			for (int i = 0; i < 8; i++) {
				int index = 8 + i;
				if (getPieceX(index, color) == x0
						&& getPieceY(index, color) == y0) {
					setPieceX(index, color, x1);
					setPieceY(index, color, y1);
				}
			}
		} else {
			for (int i = 0; i < 8; i++) {
				if (getPieceX(i, color) == x0 && getPieceY(i, color) == y0) {
					setPieceX(i, color, x1);
					setPieceY(i, color, y1);
				}
			}
		}

		// --Update the taken piece's location
		if (takenType != NO_TYPE) {
			int takenColor = getPieceColor(x1, y1);
			if (takenType == PAWN) {
				for (int i = 0; i < 8; i++) {
					int index = 8 + i;
					if (getPieceX(index, takenColor) == x1
							&& getPieceY(index, takenColor) == y1) {
						setPieceX(index, takenColor, NO_X_COR);
						setPieceY(index, takenColor, NO_Y_COR);
						break;
					}
				}
			} else {
				for (int i = 0; i < 8; i++) {
					if (getPieceX(i, takenColor) == x1
							&& getPieceY(i, takenColor) == y1) {
						setPieceX(i, takenColor, NO_X_COR);
						setPieceY(i, takenColor, NO_Y_COR);
						break;
					}
				}
			}
		}

		// --Make the move
		setPieceType(x0, y0, NO_TYPE);
		setPieceType(x1, y1, type);
		setPieceColor(x1, y1, color);
	}

	private final boolean isBlock(char x, int y){
		return getPieceType(x,y) != NO_TYPE;
	}
	
	private final boolean isBlock(char x, int y, int color) {
		return getPieceType(x, y) != NO_TYPE && getPieceColor(x, y) == color;
	}

	private final boolean isTakable(char x, int y, int color) {
		return getPieceType(x, y) != NO_TYPE && getPieceColor(x, y) != color;
	}

	private final long rookMoves(char x, int y, int color,
			Collection<ChessPosition> list, long attackMatrix) {
		// --Horizontal moves
		// (right)
		for (char newX = (char) (x + 1); newX <= 'h'; newX++) {
			if (isBlock(newX, y, color)) {
				break;
			} else {
				list.add(move(x, y, newX, y));
				attackMatrix = setAttacked(attackMatrix, newX, y);
			}
			if (isTakable(newX, y, color)) {
				break;
			}
		}
		// (left)
		for (char newX = (char) (x - 1); newX >= 'a'; newX--) {
			if (isBlock(newX, y, color)) {
				break;
			} else {
				list.add(move(x, y, newX, y));
				attackMatrix = setAttacked(attackMatrix, newX, y);
			}
			if (isTakable(newX, y, color)) {
				break;
			}
		}
		// --Vertical moves
		// (up)
		for (int newY = y + 1; newY <= 8; newY++) {
			if (isBlock(x, newY, color)) {
				break;
			} else {
				list.add(move(x, y, x, newY));
				attackMatrix = setAttacked(attackMatrix, x, newY);
			}
			if (isTakable(x, newY, color)) {
				break;
			}
		}
		// (down)
		for (int newY = y - 1; newY >= 1; newY--) {
			if (isBlock(x, newY, color)) {
				break;
			} else {
				list.add(move(x, y, x, newY));
				attackMatrix = setAttacked(attackMatrix, x, newY);
			}
			if (isTakable(x, newY, color)) {
				break;
			}
		}

		return attackMatrix;
	}

	private final long knightMoves(char x, int y, int color,
			Collection<ChessPosition> list, long attackMatrix) {
		char newX;
		int newY;
		// --2 left
		newX = (char) (x - 2);
		// (move 1)
		newY = y - 1;
		if (newX >= 'a' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 2)
		newY = y + 1;
		if (newX >= 'a' && newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// --1 left
		newX = (char) (x - 1);
		// (move 3)
		newY = y - 2;
		if (newX >= 'a' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 4)
		newY = y + 2;
		if (newX >= 'a' && newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// --1 right
		newX = (char) (x + 1);
		// (move 3)
		newY = y - 2;
		if (newX <= 'h' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 4)
		newY = y + 2;
		if (newX <= 'h' && newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// --2 right
		newX = (char) (x + 2);
		// (move 3)
		newY = y - 1;
		if (newX <= 'h' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 4)
		newY = y + 1;
		if (newX <= 'h' && newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}

		return attackMatrix;
	}

	private final long bishopMoves(char x, int y, int color,
			Collection<ChessPosition> list, long attackMatrix) {
		// --ascending diagonal
		int start = Math.min(x - 'a', y - 1);
		int stop = Math.min('h' - x, 8 - y);
		// (upper branch)
		for (int dif = 1; dif <= stop; dif++) {
			char newX = (char) (x + dif);
			int newY = y + dif;
			if (isBlock(newX, newY, color)) {
				break;
			} else {
//				System.out.println("ascending diagonal upper");
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
			if (isTakable(newX, newY, color)){
				break;
			}
		}
		// (lower branch)
		for (int dif = 1; dif <= start; dif++) {
			char newX = (char) (x - dif);
			int newY = y - dif;
			if (isBlock(newX, newY, color)) {
				break;
			} else {
//				System.out.println("ascending diagonal lower");
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
			if (isTakable(newX, newY, color)){
				break;
			}
		}

		// --Decending diagonal
		start = Math.min(x - 'a', 8 - y);
		stop = Math.min('h' - x, y - 1);
		// (upper branch)
		for (int dif = 1; dif <= start; dif++) {
			char newX = (char) (x - dif);
			int newY = y + dif;
			if (isBlock(newX, newY, color)) {
				break;
			} else {
//				System.out.println("decending diagonal upper");
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
			if (isTakable(newX, newY, color)){
				break;
			}
		}
		// (lower branch)
		for (int dif = 1; dif <= stop; dif++) {
			char newX = (char) (x + dif);
			int newY = y - dif;
			if (isBlock(newX, newY, color)) {
				break;
			} else {
//				System.out.println("decending diagonal lower");
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
			if (isTakable(newX, newY, color)){
				break;
			}
		}
		return attackMatrix;
	}

	private final long queenMoves(char x, int y, int color,
			Collection<ChessPosition> list, long attackMatrix) {
		attackMatrix = rookMoves(x, y, color, list, attackMatrix);
		attackMatrix = bishopMoves(x, y, color, list, attackMatrix);
		return attackMatrix;
	}

	private final long kingMoves(char x, int y, int color,
			Collection<ChessPosition> list, long attackMatrix) {
		char newX;
		int newY;
		// --X left 1
		// (move 1)
		newX = (char) (x - 1);
		newY = y - 1;
		if (newX >= 'a' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 2)
		newY = y;
		if (newX >= 'a' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 3)
		newY = y + 1;
		if (newX >= 'a' && newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}

		// --X stays same
		// (move 4)
		newX = x;
		newY = y - 1;
		if (newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 8)
		newX = x;
		newY = y + 1;
		if (newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}

		// --X right one
		// (move 1)
		newX = (char) (x + 1);
		newY = y - 1;
		if (newX <= 'h' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 2)
		newY = y;
		if (newX <= 'h' && newY >= 1) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}
		// (move 3)
		newY = y + 1;
		if (newX <= 'h' && newY <= 8) {
			if (!isBlock(newX, newY, color)) {
				list.add(move(x, y, newX, newY));
				attackMatrix = setAttacked(attackMatrix, newX, newY);
			}
		}

		return attackMatrix;
	}

	private final void promote(char x0, int y0, char x1, int y1,
			Collection<ChessPosition> list) {
		ChessPosition pos;
		// (knight)
		pos = move(x0, y0, x1, y1);
		pos.setPieceType(x1, y1, KNIGHT);
		list.add(pos);
		// (bishop)
		pos = move(x0, y0, x1, y1);
		pos.setPieceType(x1, y1, BISHOP);
		list.add(pos);
		// (rook)
		pos = move(x0, y0, x1, y1);
		pos.setPieceType(x1, y1, ROOK);
		list.add(pos);
		// (queen)
		pos = move(x0, y0, x1, y1);
		pos.setPieceType(x1, y1, QUEEN);
		list.add(pos);
	}

	private final long pawnMoves(char x, int y, int color,
			Collection<ChessPosition> list, long attackMatrix) {
		char newX;
		int newY;
		if (color == WHITE) {
			// --Normal moves
			// (one move ahead)
			newY = y + 1;
			if (newY < 8) {
				if (!isBlock(x, newY)) {
					list.add(move(x, y, x, newY));
				}
			} else if (newY == 8) {
				if (!isBlock(x, newY)) {
					promote(x, y, x, newY, list);
				}
			}
			// (two moves ahead)
			if (y == 2) {
				newY = y + 2;
				if (!isBlock(x, y + 1) && !isBlock(x, newY)) {
					list.add(move(x, y, x, newY));
				}
			}
			// (takes)
			newX = (char) (x - 1);
			newY = y + 1;
			if (newX >= 'a' && newY <= 8) {
				attackMatrix = setAttacked(attackMatrix, newX, newY);
				if (isTakable(newX, newY, color)) {
					list.add(move(x, y, newX, newY));
				}
			}
			newX = (char) (x + 1);
			newY = y + 1;
			if (newX <= 'h' && newY <= 8) {
				attackMatrix = setAttacked(attackMatrix, newX, newY);
				if (isTakable(newX, newY, color)) {
					list.add(move(x, y, newX, newY));
				}
			}
		} else {
			// --Normal moves
			// (one move ahead)
			newY = y - 1;
			if (newY > 1) {
				if (!isBlock(x, newY)) {
					list.add(move(x, y, x, newY));
				}
			} else if (newY == 1) {
				if (!isBlock(x, newY)) {
					promote(x, y, x, newY, list);
				}
			}
			// (two moves ahead)
			if (y == 7) {
				newY = y - 2;
				if (!isBlock(x, y - 1) && !isBlock(x, newY)) {
					list.add(move(x, y, x, newY));
				}
			}
			// (takes)
			newX = (char) (x - 1);
			newY = y - 1;
			if (newX >= 'a' && newY >= 0) {
				attackMatrix = setAttacked(attackMatrix, newX, newY);
				if (isTakable(newX, newY, color)) {
					list.add(move(x, y, newX, newY));
				}
			}
			newX = (char) (x + 1);
			newY = y - 1;
			if (newX <= 'h' && newY >= 0) {
				attackMatrix = setAttacked(attackMatrix, newX, newY);
				if (isTakable(newX, newY, color)) {
					list.add(move(x, y, newX, newY));
				}
			}
		}
		return attackMatrix;

	}

	private final String squareToString(char x, int y, boolean longBlank) {
		String rtn = "";
		if (getPieceColor(x, y) == WHITE) {
			rtn += "W";
		} else {
			rtn += "B";
		}
		switch (getPieceType(x, y)) {
		case PAWN:
			rtn += "P";
			break;
		case ROOK:
			rtn += "R";
			break;
		case KNIGHT:
			rtn += "N";
			break;
		case BISHOP:
			rtn += "B";
			break;
		case QUEEN:
			rtn += "Q";
			break;
		case KING:
			rtn += "K";
			break;
		case NO_TYPE:
			if(longBlank){
				return "    ";
			}else{
				return "  ";
			}
		default:
			throw new IllegalStateException("Invalid piece type");
		}
		return rtn;
	}

	public final void printToConsole() {
		for (int y = 8; y >= 1; y--) {
			for (char x = 'a'; x <= 'h'; x++) {
				System.out.print("| " + squareToString(x, y, false) + " ");
			}
			System.out.println("|");
			System.out.println();
		}
	}
	
	public final void printToConsole(Console c) {
		for (int y = 8; y >= 1; y--) {
			for (char x = 'a'; x <= 'h'; x++) {
				c.print("| " + squareToString(x, y, true) + " ");
			}
			c.println("|");
			c.println("");
		}
	}
	
	public final int getPointVal(char x, int y){
		switch(getPieceType(x,y)){
		case PAWN:
			return 1;
		case KNIGHT:
			return 3;
		case BISHOP:
			return 3;
		case ROOK:
			return 5;
		case QUEEN:
			return 6;
		case KING:
			return 0;
		default:
			return 0;
		}
	}

	public final void setScore(int cost, int heuristic) {
		this.score = cost + heuristic;
	}

	public final int getScore() {
		return score;
	}
	
	public final ChessPosition copy() {
		ChessPosition rtn = new ChessPosition(false);
		rtn.score = this.score;
		rtn.data = Arrays.copyOf(this.data, this.data.length);
		return rtn;
	}

	public final ChessPosition move(char x0, int y0, char x1, int y1) {
		ChessPosition rtn = copy();
		rtn.internalMove(x0, y0, x1, y1);
		return rtn;
	}

	public final ChessPosition move(ChessMove move) {
		return move(move.x0, move.y0, move.x1, move.y1);
	}

	public final PositionInformation calculateInformation(int color) {
		// --Children and attack matrix
		Collection<ChessPosition> children = new ArrayList<ChessPosition>();
		long attackMatrix = 0x0000000000000000;
		for (int i = 0; i < 16; i++) {
			char x = getPieceX(i, color);
			if (x != NO_X_COR) {
				int y = getPieceY(i, color);
				switch (i) {
				case 0:
				case 7:
					attackMatrix = rookMoves(x, y, color, children, attackMatrix);
					break;
				case 1:
				case 6:
					attackMatrix = knightMoves(x, y, color, children, attackMatrix);
					break;
				case 2:
				case 5:
					attackMatrix = bishopMoves(x, y, color, children, attackMatrix);
					break;
				case 3:
					attackMatrix = queenMoves(x, y, color, children, attackMatrix);
					break;
				case 4:
					attackMatrix = kingMoves(x, y, color, children, attackMatrix);
					break;
				default:
					attackMatrix = pawnMoves(x, y, color, children, attackMatrix);
					break;
				}
			}
		}
		// --Prepare return
		PositionInformation info = new PositionInformation(color);
		info.children = children;
		info.attackMatrix = attackMatrix;
		return info;
	}

	@Override
	public boolean equals(Object o) {
		if (o instanceof ChessPosition) {
			return Arrays.equals(data, ((ChessPosition) o).data);
		} else {
			return false;
		}
	}

	@Override
	public int hashCode() {
		return Arrays.hashCode(data);
	}

	@Override
	public String toString() {
		return Arrays.toString(data);
	}

	@Override
	public int compareTo(ChessPosition p2) {
		return (int) (this.getScore() - p2.getScore());
	}
	
	private final static int opponent(int color){
		return (color+1)%2;
	}
	
	private static final boolean isTrue(long data, char x, int y){
		int shift = 8 * (x - 'a') + (y - 1);
		long mask = 0x0000000000000001L << shift;
		if ((data & mask) >> shift == 1) {
			return true;
		} else {
			return false;
		}
	}

	public final static int getType(int index) {
		switch (index) {
		case 0:
		case 7:
			return ROOK;
		case 1:
		case 6:
			return KNIGHT;
		case 2:
		case 5:
			return BISHOP;
		case 3:
			return QUEEN;
		case 4:
			return KING;
		default:
			return PAWN;
		}
	}

	public final static String getTypeName(int index) {
		switch (index) {
		case 0:
		case 7:
			return "ROOK";
		case 1:
		case 6:
			return "KNIGHT";
		case 2:
		case 5:
			return "BISHOP";
		case 3:
			return "QUEEN";
		case 4:
			return "KING";
		default:
			return "PAWN";
		}
	}

	public final static double getPointVal(int index) {
		switch (index) {
		case 0:
		case 7:
			return 5;
		case 1:
		case 6:
			return 3;
		case 2:
		case 5:
			return 3;
		case 3:
			return 9;
		case 4:
			return 0;
		default:
			return 1;
		}
	}
	
	public final static double getImportanceVal(int index){
		switch (index) {
		case 0:
		case 7:
			return 5;
		case 1:
		case 6:
			return 3;
		case 2:
		case 5:
			return 3;
		case 3:
			return 9;
		case 4:
			return Double.POSITIVE_INFINITY;
		default:
			return 1;
		}
	}

	public final static ChessMove subtract(ChessPosition a, ChessPosition b) {
		char fromX='i', toX='i';
		int fromY=-1, toY=-1;
		for (int y = 8; y >= 1; y--) {
			for (char x = 'a'; x <= 'h'; x++) {
				// (get information)
				int oldType = a.getPieceType(x, y);
				int newType = b.getPieceType(x, y);
				int oldColor = a.getPieceColor(x, y);
				int newColor = b.getPieceColor(x, y);
				// (check if this is the from piece)
				if (oldType != NO_TYPE && newType == NO_TYPE) {
					fromX = x;
					fromY = y;
				}
				// (check if this is the to piece)
				if (newType != NO_TYPE
						&& (newType != oldType || (newType == oldType && newColor != oldColor))) {
					toX = x;
					toY = y;
				}
			}
		}
		return new ChessMove(fromX, fromY, toX, toY);
	}

	public static void main(String[] args) {
		ChessPosition p = new ChessPosition(7);
		
		PositionInformation info = p.calculateInformation(WHITE);
		for(ChessPosition x : info.getChildren()){
			x.printToConsole();
			System.out.println();
		}
		
//		p.vanishPiece(8, WHITE);
//		p.printToConsole();
//		PositionInformation info = p.calculateInformation(WHITE);
//		System.out.println( info.getPoints(WHITE) );
//		System.out.println( info.getPoints(BLACK) );
	}

}
